觀察style.css
中,可以發現作者幫我們寫了一個highlight
樣式,並且使用了position: absolute;
脫離文檔流,但index.html
中並沒有一個元素使用這個 class name,因此我們應該是用 javascript 創造出一個元素套用上這個 class name 並隨著各個連結移動達到的聚光燈效果。
.highlight {
transition: all 0.2s;
border-bottom: 2px solid white;
position: absolute;
top: 0;
background: white;
left: 0;
z-index: -1;
border-radius: 20px;
display: block;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
}
const body = document.querySelector("body");
const highlight = document.createElement("div");
highlight.classList.add("highlight");
body.appendChild(highlight);
<a>
節點,並每個都要新增mouseenter
事件監聽器,當鼠標移入元素時觸發handleHighlight
函式。// 取得所有`<a>`節點
const linkList = document.querySelectorAll("a");
// 全部新增`mouseenter`事件監聽器
linkList.forEach((link) =>
link.addEventListener("mouseenter", handleHighlight)
);
highlight
的定位,要基於觸發事件的那個<a>
移動。hightlight
的定位top
、left
,但你用了會發現只有<nav>
中連結的效果是我們想要的,而所有<div class=wrapper">
中的連結,都有所偏移,我們也可以印出來觀察看看。function handleHighlight(e) {
highlight.style.top = `${this.offsetTop}px`;
highlight.style.left = `${this.offsetLeft}px`;
console.log("offsetTop", this.offsetTop, "offsetLeft", this.offsetLeft);
highlight.style.width = `${this.offsetWidth}px`;
highlight.style.height = `${this.offsetHeight}px`;
}
打開 Devtool 觀察你會發現,當滑鼠移至<a href="">consectetur</a>
上時聚光燈的效果偏移了,不是我們想要的樣子,且印出來的offsetTop
只有 6offsetLeft
只有 269,這是為什麼呢?這時候我們先回去 MDN 在仔細看一次offsetTop的敘述「returns the distance of the outer border of the current element relative to the inner border of the top of the offsetParent」,那我們改印觸發事件的offsetParent
出來看看。
這時你會發現:
<nav>
內連結的 offsetParent 是<body>
<div class="wrapper">
內連結的 offsetParent 是<div class="wrapper">
什麼樣的原因造成的呢?答案是<div class="wrapper">
容器上的positon:relative
,因此這個容器內的所有<a>
定位依據都指向了它,你可能會想那我再加個this.offsetParent
的offsetTop/Left
不就正常了嗎?
highlight.style.top = `${this.offsetTop + this.offsetParent.offsetTop}px`;
highlight.style.left = `${this.offsetLeft + this.offsetParent.offsetLeft}px`;
但如果遇上層層結構每層都有positon:relative
設定,那簡直沒完沒了...
最後依照上面所述改寫handleHighlight
函式,即完成本日目標
function handleHighlight(e) {
highlight.style.top = `${this.getBoundingClientRect().top + scrollY}px`;
highlight.style.left = `${this.getBoundingClientRect().left + scrollX}px`;
highlight.style.width = `${this.offsetWidth}px`;
highlight.style.height = `${this.offsetHeight}px`;
}
(圖片從MDN引用)